#include "math.h"


// Cache for FastLn
double * LN_CACHE = NULL;
const uint64 kLnCacheBits = 16;
const uint64 kLookupBits = kLnCacheBits - 1;
const uint64 kFractionBits = 52 - kLookupBits;
const double kFractionMultiplier = 1.0 / (1ull << kFractionBits);
const uint64 kLnCacheSize = (1 << kLnCacheBits) + 1;

// Cache for FastLnFactorial
double * LN_FACTORIAL_CACHE = NULL;
const uint64 kLnFactorialCacheSize = 32768;
double kLnFactorialAdjust = 0;

// Cache for LnSumHelper
double * LN_SUM_CACHE = NULL;
const int64 kLnSumMin = 36; // -36
const uint64 kLnSumScale = 10000;
const uint64 kLnSumCacheSize = kLnSumMin * kLnSumScale + 1;

double LnStirling(uint64 n) { return n * FastLn(n) - n; }

void InitMath() {
  if (LN_CACHE) return;

  // Ln Cache
  LN_CACHE = new double[kLnCacheSize];
  for (uint32 i=1; i<kLnCacheSize; i++)
    LN_CACHE[i] = log(i);
  
  // Ln Factorial Cache
  LN_FACTORIAL_CACHE = new double[kLnFactorialCacheSize+1];
  LN_FACTORIAL_CACHE[0] = 0;
  for (uint64 i=1; i<=kLnFactorialCacheSize; i++) {
    LN_FACTORIAL_CACHE[i] = LN_FACTORIAL_CACHE[i-1] + log(i);
  }
  kLnFactorialAdjust 
    = LN_FACTORIAL_CACHE[kLnFactorialCacheSize] 
    - LnStirling(kLnFactorialCacheSize);

  // LnSum Cache
  LN_SUM_CACHE = new double[kLnSumCacheSize];
  for (uint32 i=0; i<kLnSumCacheSize; i++) {
    double real = (double)i / kLnSumScale - kLnSumMin;
    LN_SUM_CACHE[i] = log(1 + exp(real));
  }
}

double FastLn(uint64 n, bool interpolate) {
  if (n == 0) return NINF; // may be bad... 
  if (n < kLnCacheSize) return LN_CACHE[n];
  double d = n;
  uint64 int_d = *((uint64 *)&d);
  int64 expt = ( (int_d >> 52) & 0x7FFull ) - 1023;
  int64 lookup = ( (int_d >> kFractionBits) & ( (1 << kLookupBits) - 1))
    + (1 << kLookupBits);
  if (!interpolate) return (expt - kLookupBits) * kLn2 + LN_CACHE[lookup];
  int64 fraction = int_d & ((1ull << kFractionBits) - 1);
  return (expt - kLookupBits) * kLn2 + 
    ((LN_CACHE[lookup] * ((1ull << kFractionBits) - fraction)
      + LN_CACHE[lookup+1] * fraction) * kFractionMultiplier);
}


double FastLnFactorial(uint64 n) {
  if (n <= kLnFactorialCacheSize) return LN_FACTORIAL_CACHE[n];
  return LnStirling(n) + kLnFactorialAdjust;
}

double LnFactorial(uint64 n) {
  double sum = 0;
  for (uint64 i=2; i <= n; i++)
    sum += log(i);
  return sum;
}

// how many bits does it take to store numbers <n
uint64 CeilingLg2(uint64 n) {
  if (n==0) return 0;
  n--;
  uint64 ret = 0;
  while (n) {
    n >>= 1;
    ret++;
  }
  return ret;
}

// returns ln(1 + exp(x)) for x<0.
inline double LnSumHelper(double x) {
  if (x < -36) return 0;
  return log(1 + exp(x));
}

double LnSum(double a, double b) {
  if (b > a) return LnSum(b, a);
  if (b == NINF) return a;
  return a + LnSumHelper(b-a);
}

// returns ln(1 + exp(x)) for x<0.
inline double FastLnSumHelper(double x) {
  CHECK(x <= 0);
  if (x < -kLnSumMin) return 0;
  uint64 index = ((kLnSumMin + x) * kLnSumScale);
  // cout << "LN_SUM_CACHE[" << index << "] = " << LN_SUM_CACHE[index] << endl;
  return LN_SUM_CACHE[index];
}

inline double FastLnSum(double a, double b) {
  if (b > a) return FastLnSum(b, a);
  if (b == NINF) return a;
  return a + FastLnSumHelper(b-a);
}

int64 SelfModelingPartialDL(int64 in_count, int64 out_count, 
			    int64 in_total, int64 out_total) {
  int64 ll = 0;
  if (in_count) {
    ll += MLnF(out_count + in_count - 1);
    ll -= MLnF(in_count - 1);
  } else {
    CHECK(out_count == 0);
  }
  if (in_total) {
    ll -= MLnF(out_total + in_total - 1);
    ll += MLnF(in_total - 1);
  } else {
    CHECK(out_total == 0);
  }
  return -ll;
}



void ExploreMath() {
  InitMath();
  string command;
  int64 i;
  double d, e;
  cout.precision(18);
  while (cin >> command) {
    if (command == "ln") {
      cin >> i;
      cout << "FastLn(" << i << ") = " << FastLn(i) << endl;
      cout << "log(" << i << ") = " << log(i) << endl;
      cout << "diff = " << FastLn(i) - log(i) << endl;
    }
    if (command == "lnf") {
      cin >> i;
      cout << "FastLnFactorial(" << i << ") = " << FastLnFactorial(i) << endl;
      cout << "LnFactorial(" << i << ") = " << LnFactorial(i) << endl;
      cout << "diff = " << FastLnFactorial(i) - LnFactorial(i) << endl;
    }
    if (command == "ln+") {
      cin >> d >> e;
      cout << "FastLnSum (" << d << ", " << e << ") = " 
	   << FastLnSum(d, e) << endl;
      cout << "LnSum (" << d << ", " << e << ") = " << LnSum(d, e) << endl;
      cout << "diff = " << FastLnSum(d, e) - LnSum(d, e) << endl;
    }
    if (command == "lsh") {
      cin >> d;
      cout << "FastLnSumHelper (" << d << ") = " << FastLnSumHelper(d) << endl;
      cout << "LnSumHelper (" << d << ") = " << LnSumHelper(d) << endl;
      cout << "diff = " << FastLnSumHelper(d) - LnSumHelper(d) << endl;
    }
    if (command == "+") {
      cin >> d >> e;
      double ld = log(d); double le = log(e);
      cout << "FastLnSum(" << ld << "," << le << ") = "
	   << FastLnSum(ld, le) << endl;
      cout << d << " + " << e << " = " << exp(FastLnSum(ld, le)) << endl;
    }
  }
}

int math_main() {
  ExploreMath();
  return 0;
}

